package org.korifey.ffbdd.impl.op;

import org.korifey.ffbdd.api.IBddBackend;
import org.korifey.ffbdd.api.INode;
import org.korifey.ffbdd.api.op.IOpCachePair;
import org.korifey.ffbdd.api.op.OpCacheKey;
import org.korifey.ffbdd.api.op.OpCacheKey.Binary;
import org.korifey.ffbdd.api.op.OpCacheKey.Unary;
import org.korifey.ffbdd.impl.BitvectorConst;
import org.korifey.ffbdd.impl.NodeImpl;

import static org.korifey.ffbdd.common.util.ByteUtil.*;
import static org.korifey.ffbdd.impl.BitvectorConst.*;

public interface OpCacheKeyBitvector {
			
	public static class BinaryBitvector {


		public static void convertToBytes(Binary key, INode value, byte[] arr, int offset) {
			int leftIdx = fakeIndex(((NodeImpl)key.left()).getIndex());
			int rightIdx = fakeIndex(((NodeImpl)key.right()).getIndex());
			int valueIdx = fakeIndex(((NodeImpl)value).getIndex());
			
			int remLeft = intToThreeBytesAndHalf(leftIdx, arr, offset);
			int remRight = intToThreeBytesAndHalf(rightIdx, arr, offset+3);
			int remValue = intToThreeBytesAndHalf(valueIdx, arr, offset+6);
			int remOpcode = intToOneByteAndHalf(key.opcode(), arr, offset+9);
			
			arr[offset+10] = twoHalfsToByte(remLeft, remRight);
			arr[offset+11] = twoHalfsToByte(remValue, remOpcode);
		}
		
		
		public static IOpCachePair parse(byte[] arr, int offset, IBddBackend backend) {
			int remLeft = halfByteToInt(arr[offset+10], true);
			int remRight = halfByteToInt(arr[offset+10], false);
			int remValue = halfByteToInt(arr[offset+11], true);
			int remOpcode = halfByteToInt(arr[offset+11], false);
			
			
			int leftIdx = threeByteAndHalfToInt(arr[offset], arr[offset+1], arr[offset+2], remLeft);
			int rightIdx = threeByteAndHalfToInt(arr[offset+3], arr[offset+4], arr[offset+5], remRight);
			int valueIdx = threeByteAndHalfToInt(arr[offset+6], arr[offset+7], arr[offset+8], remValue);
			int opcode = oneByteAndHalfToInt(arr[offset+9], remOpcode);
			
			Binary key = new Binary(
					backend.getNode(realIndex(leftIdx)),
					backend.getNode(realIndex(rightIdx)),
					opcode);
			
			return new OpCachePairBitvector(key, backend.getNode(realIndex(valueIdx)));
		}

		
	}
	
	
	
	
	public static class UnaryBitvector {		

		public static void convertToBytes(Unary key, INode value, byte[] arr, int offset) {
			int leftIdx = BitvectorConst.fakeIndex(((NodeImpl)key.node()).getIndex());			
			int valueIdx = BitvectorConst.fakeIndex(((NodeImpl)value).getIndex());
			
			int remLeft = intToThreeBytesAndHalf(leftIdx, arr, offset);			
			int remValue = intToThreeBytesAndHalf(valueIdx, arr, offset+6);
			int remOpcode = intToOneByteAndHalf(key.opcode(), arr, offset+9);
			
			arr[offset+10] = twoHalfsToByte(remLeft, 0);
			arr[offset+11] = twoHalfsToByte(remValue, remOpcode);			
		}
		
		public static IOpCachePair parse(byte[] arr, int offset, IBddBackend backend) {
			int remLeft = halfByteToInt(arr[offset+10], true);			
			int remValue = halfByteToInt(arr[offset+11], true);
			int remOpcode = halfByteToInt(arr[offset+11], false);
			
			
			int leftIdx = threeByteAndHalfToInt(arr[offset], arr[offset+1], arr[offset+2], remLeft);			
			int valueIdx = threeByteAndHalfToInt(arr[offset+6], arr[offset+7], arr[offset+8], remValue);
			int opcode = oneByteAndHalfToInt(arr[offset+9], remOpcode);
			
			Unary key = new Unary(
					backend.getNode(realIndex(leftIdx)),					
					opcode);
			
			return new OpCachePairBitvector(key, backend.getNode(realIndex(valueIdx)));
		}		
	}
	
	
}
